﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DataManager.cs" company="">
//   
// </copyright>
// <summary>
//   The data manager.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Timers;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

namespace Systemathics.FrameWork 
{
    public sealed class DataManager
    {
        // 1 méga-octet (Mo) = 2^20 octets = 1 024 Ko = 1 048 576 octets
        private const long MaxSize = 1048576; 
        private const long SPAN = 1000 * 60 * 3; // 3 Minutes
        private readonly BackgroundWorker BackgroundWorker;
        private BinaryFormatter BinaryFormatter;
        private readonly Guid[] DataManagerInstruments;
        private readonly Dictionary<Guid, MemoryStream> DynamicStreams;
        private readonly Dictionary<Guid, TickCaptures> queues;
        private readonly RemotingLogClient remote;
        private readonly Timer Timer;
        private Dictionary<Guid, String> InstrumentsName;
        public DataManager(Guid[] Instruments)
        {
            remote = new RemotingLogClient();
            DataManagerInstruments = Instruments;
            InstrumentsName = new Dictionary<Guid, string>();
            queues = new Dictionary<Guid, TickCaptures>();
            DynamicStreams = new Dictionary<Guid, MemoryStream>();
            using (var connection = new SqlConnection(GlobalVariables.SqlConnectionString(GlobalVariables.TICKCAPTURES_DATABASE_NAME)))
            {
                var server = new Server(new ServerConnection(connection));
                Database db = server.Databases[GlobalVariables.TICKCAPTURES_DATABASE_NAME];
                foreach (Guid Guid in DataManagerInstruments)
                {
                    var TableName = Guid.ToString();
                    DynamicStreams[Guid] = new MemoryStream();
                    if (!db.Tables.Contains(TableName))
                    {
                        var newTable = new Table(db, TableName);
                        var IDColumn = new Column(newTable, "ID");
                        IDColumn.DataType = DataType.Int;
                        IDColumn.Nullable = false;
                        IDColumn.Identity = true;
                        IDColumn.IdentitySeed = 1;
                        IDColumn.IdentityIncrement = 1;
                        var StartDateColumn = new Column(newTable, "STARTDATE");
                        StartDateColumn.DataType = DataType.DateTime;
                        var EndDateColumn = new Column(newTable, "ENDDATE");
                        EndDateColumn.DataType = DataType.DateTime;
                        var BinaryColumn = new Column(newTable, "DATA");
                        BinaryColumn.DataType = DataType.Image;
                        newTable.Columns.Add(IDColumn);
                        newTable.Columns.Add(StartDateColumn);
                        newTable.Columns.Add(EndDateColumn);
                        newTable.Columns.Add(BinaryColumn);
                        string PrimaryKey = "PK_" + newTable;
                        var index = new Index(newTable, PrimaryKey);
                        index.IndexKeyType = IndexKeyType.DriPrimaryKey;
                        index.IndexedColumns.Add(new IndexedColumn(index, "ID"));
                        newTable.Indexes.Add(index);
                        newTable.Create();
                    }
                    else
                    {
                        string Query = "SELECT DATA FROM [" + TableName + "] WHERE CAST(STARTDATE AS DATE) = CAST(GETDATE() AS DATE)";
                        var command = new SqlCommand(Query, connection);
                        SqlDataReader rdr = command.ExecuteReader();
                        if (rdr.HasRows)
                        {
                            rdr.Read();
                            var buffer = rdr.GetValue(0) as byte[];
                            DynamicStreams[Guid] = new MemoryStream(buffer);
                            DynamicStreams[Guid].Position = DynamicStreams[Guid].Length + 1;
                            rdr.Close();
                        }
                    }
                    queues[Guid] = new TickCaptures();
                    isEnabled = true;
                }
                BinaryFormatter = new BinaryFormatter();
                BackgroundWorker = new BackgroundWorker {WorkerSupportsCancellation = true};
                BackgroundWorker.DoWork += BackgroundWorkerDoWork;
                Timer = new Timer(SPAN);
                Timer.Elapsed += TimerElapsed;
                Timer.Start();
                connection.Close();
            }
            using (var db = new SystemathicsEntities(GlobalVariables.EntitiesConnectionString()))
            {
                foreach (var guid in DataManagerInstruments)
                    InstrumentsName[guid] = db.DBInstruments.Where(u => u.ID == guid).FirstOrDefault().Name;
            }
        }


        public bool isEnabled { get; private set; }
        public void Close()
        {
            isEnabled = false;
            Serializer();
            BackgroundWorker.CancelAsync();
            BackgroundWorker.Dispose();
            Timer.Stop();
            foreach (var kvp in DynamicStreams)
            {
                if(kvp.Value.CanRead)
                {
                    if (kvp.Value.Length > 100 * 1024) // 100Ko
                    {
                        queues[kvp.Key].Clear();
                        kvp.Value.Flush();
                        kvp.Value.Close();
                        kvp.Value.Dispose();
                    }
                }
            }
        }
        internal void Write(Blotter blotter)
        {
            lock (queues[blotter.Id])
            {
                if ((blotter.TickInfo & TickInfo.Ask_1) == TickInfo.Ask_1)
                    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Ask_1, blotter[QuoteLevel.One].Ask);
               
                if ((blotter.TickInfo & TickInfo.AskSize_1) == TickInfo.AskSize_1)
                    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.AskSize_1, blotter[QuoteLevel.One].AskSize);
                
                if ((blotter.TickInfo & TickInfo.Bid_1) == TickInfo.Bid_1)
                    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Bid_1, blotter[QuoteLevel.One].Bid);
                
                if ((blotter.TickInfo & TickInfo.BidSize_1) == TickInfo.BidSize_1)
                    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.BidSize_1, blotter[QuoteLevel.One].BidSize);

                
                //if ((blotter.TickInfo & TickInfo.Ask_2) == TickInfo.Ask_2)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Ask_2, blotter[QuoteLevel.One].Ask);
                //if ((blotter.TickInfo & TickInfo.Ask_3) == TickInfo.Ask_3)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Ask_3, blotter[QuoteLevel.One].Ask);
                //if ((blotter.TickInfo & TickInfo.Ask_4) == TickInfo.Ask_4)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Ask_4, blotter[QuoteLevel.One].Ask);
                //if ((blotter.TickInfo & TickInfo.Ask_5) == TickInfo.Ask_5)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Ask_5, blotter[QuoteLevel.One].Ask);

                //if ((blotter.TickInfo & TickInfo.AskSize_2) == TickInfo.AskSize_2)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Ask_2, blotter[QuoteLevel.One].AskSize);
                //if ((blotter.TickInfo & TickInfo.AskSize_3) == TickInfo.AskSize_3)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Ask_3, blotter[QuoteLevel.One].AskSize);
                //if ((blotter.TickInfo & TickInfo.AskSize_4) == TickInfo.AskSize_4)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.AskSize_4, blotter[QuoteLevel.One].AskSize);
                //if ((blotter.TickInfo & TickInfo.AskSize_5) == TickInfo.AskSize_5)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.AskSize_5, blotter[QuoteLevel.One].AskSize);

                //if ((blotter.TickInfo & TickInfo.Bid_2) == TickInfo.Bid_2)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Bid_2, blotter[QuoteLevel.One].Bid);
                //if ((blotter.TickInfo & TickInfo.Bid_3) == TickInfo.Bid_3)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Bid_3, blotter[QuoteLevel.One].Bid);
                //if ((blotter.TickInfo & TickInfo.Bid_4) == TickInfo.Bid_4)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Bid_4, blotter[QuoteLevel.One].Bid);
                //if ((blotter.TickInfo & TickInfo.Bid_5) == TickInfo.Bid_5)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.Bid_5, blotter[QuoteLevel.One].Bid);

                //if ((blotter.TickInfo & TickInfo.BidSize_2) == TickInfo.BidSize_2)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.BidSize_2, blotter[QuoteLevel.One].BidSize);
                //if ((blotter.TickInfo & TickInfo.BidSize_3) == TickInfo.BidSize_3)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.BidSize_3, blotter[QuoteLevel.One].BidSize);
                //if ((blotter.TickInfo & TickInfo.BidSize_4) == TickInfo.BidSize_4)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.BidSize_4, blotter[QuoteLevel.One].BidSize);
                //if ((blotter.TickInfo & TickInfo.BidSize_5) == TickInfo.BidSize_5)
                //    queues[blotter.Id][Time.Now] = new TickCapture(TickInfo.BidSize_5, blotter[QuoteLevel.One].BidSize);
            }
        }
        internal void Write(Trade Trade)
        {
            lock (queues[Trade.Id])
            {
                if ((Trade.TickInfo & TickInfo.LastTrade) > 0)
                    queues[Trade.Id][Time.Now] = new TickCapture(TickInfo.LastTrade, Trade.Price);
                if ((Trade.TickInfo & TickInfo.LastSize) > 0)
                    queues[Trade.Id][Time.Now] = new TickCapture(TickInfo.LastSize, Trade.Size);
            }
        }
        internal void Write(MarketData marketData)
        {
            lock (queues[marketData.Id])
            {
                queues[marketData.Id][Time.Now] = new TickCapture(TickInfo.Open, marketData.Open);
                queues[marketData.Id][Time.Now] = new TickCapture(TickInfo.Close, marketData.Close);
            }
        }
        private void Save()
        {

            using (var connection = new SqlConnection(GlobalVariables.SqlConnectionString(GlobalVariables.TICKCAPTURES_DATABASE_NAME)))
            {
                long Length = 0;
                var server = new Server(new ServerConnection(connection));
                Database db = server.Databases[GlobalVariables.TICKCAPTURES_DATABASE_NAME];
                foreach (Guid Guid in DataManagerInstruments)
                {
                    if (DynamicStreams[Guid].Length > MaxSize)
                    {
                        lock (DynamicStreams[Guid])
                        {
                            DynamicStreams[Guid].Flush();
                            var fixedSizeMemStream = new MemoryStream(DynamicStreams[Guid].ToArray(), 0,(int) DynamicStreams[Guid].Length, false, true);

                            // DynamicStreams[ID] = new MemoryStream(); !!!!!!!!!!!!!!!!!
                            byte[] dataToStore = fixedSizeMemStream.ToArray();
                            Length += dataToStore.Length;
                            var ptrParm = new SqlParameter("@BLOBDATA", SqlDbType.Image, dataToStore.Length){Value = dataToStore,Direction = ParameterDirection.Input};

                            var a = @"SELECT ID FROM DATES WHERE Instrument ='" + Guid + "'";
                            var dataReader1 = new SqlCommand(a, connection).ExecuteReader();

                            if (dataReader1.HasRows)
                            {
                                var b = @"SELECT ID FROM [" + Guid + "] WHERE CAST(STARTDATE AS DATE) = CAST(GETDATE() AS DATE)";
                                var DataReader2 = new SqlCommand(b, connection).ExecuteReader();
                                if (DataReader2.HasRows)
                                {
                                    DataReader2.Read();
                                    var id = DataReader2.GetInt32(0);
                                    var c = @"UPDATE [" + Guid + "] SET DATA = @BLOBDATA , ENDDATE = GETDATE() WHERE ID = '" + id + "'";
                                    var cmd = new SqlCommand(c, connection);
                                    cmd.Parameters.Add(ptrParm);
                                    cmd.ExecuteNonQuery();
                                }
                                else
                                {
                                    var e = @"INSERT INTO [" + Guid + "] (STARTDATE,ENDDATE,DATA) Values(GETDATE(),GETDATE(),@BLOBDATA)";
                                    var cmd = new SqlCommand(e, connection);
                                    cmd.Parameters.Add(ptrParm);
                                    cmd.ExecuteNonQuery();
                                }
                                var d = @"UPDATE DATES SET LastEndDate = GETDATE() WHERE Instrument = '" + Guid + "'";
                                new SqlCommand(d, connection).ExecuteNonQuery();
                                DataReader2.Close();
                            }
                            else
                            {
                                var g = @"INSERT INTO [" + Guid + "] (STARTDATE,ENDDATE,DATA) Values(GETDATE(),GETDATE(),@BLOBDATA)";
                                var cmd = new SqlCommand(g, connection);
                                cmd.Parameters.Add(ptrParm);
                                cmd.ExecuteNonQuery();
                                var h = @"INSERT INTO DATES (Instrument, Name, FirstStartDate, LastEndDate) Values('" + Guid + "','" + InstrumentsName[Guid] + "',GETDATE(),GETDATE())";
                                new SqlCommand(h, connection).ExecuteNonQuery();
                            }
                            dataReader1.Close();
                            fixedSizeMemStream.Close();
                            fixedSizeMemStream.Dispose();
                        }
                    }
                }
                remote.Trace(new FireMessageEventArgs("Total Recorded Size for today : " + (Length / 1048576) + " Mo", Time.Now.TimeOfDay));
                connection.Close();
            }
        }
        private void Serializer()
        {
            try
            {
                foreach (var Guid in DataManagerInstruments)
                    if (queues[Guid].Count() > 0)
                    {
                        BinaryFormatter = new BinaryFormatter();
                        DynamicStreams[Guid].Flush();
                        lock (queues[Guid])
                            BinaryFormatter.Serialize(DynamicStreams[Guid], queues[Guid].DeepCopy());
                    }
                

                foreach (var guid in DataManagerInstruments)
                    queues[guid].Clear();

                Save();
            }
            catch (Exception ex)
            {
                
            }
        }
        private void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            if (!BackgroundWorker.IsBusy && isEnabled)
                BackgroundWorker.RunWorkerAsync();
        }
        private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            Serializer();
        }
    }
}

//.Zip
//FileStream fs = File.OpenRead(m_DailyStockFile);
//byte[] buffer = new byte[fs.Length];
//fs.Read(buffer, 0, buffer.Length);
//fs.Close();
//MemoryStream ms = new MemoryStream();, !!!;
//DeflateStream compressedZipStream = new DeflateStream(ms, CompressionMode.Compress, true);
//compressedZipStream.Write(buffer, 0, buffer.Length);
//compressedZipStream.Close();
//FileStream fs_zip = File.Create(m_DailyStockFile + ".zip");
//ms.WriteTo(fs_zip);
//ms.Close();
//fs_zip.Flush();
//fs_zip.Close();